package agent.strategy;

import agent.Agent;
//import agent.strategy.nextDirObject;
import oracle.MoveTypes;
import oracle.Oracle;

/**
 * This class is responsible for doing the optimal move when it is advised that 
 * we need to exit the map or just to move where we expect to optimize our utility
 * The key method is getNearestTerminal --- and pass it a flag saying what you want
 * 
 * this says I do not know what to do if there are no terminals --> ??? any thoughts on this
 * 
 * This should update parameters that says if we are to do each action what is the expected utility
 * 
 * @author mdailey
 *
 */
public class TerminalStrategy extends Strategy {
	// if we want to get to the nearest terminal we do this
	nextDirObject ifNearestTerm = new nextDirObject();
	// if we want to kill ourselves we do this
	nextDirObject ifSuicide = new nextDirObject();
	
	
	/** 
	 * Our constructor which calls our super constructor
	 * @param myBoard
	 */
	public TerminalStrategy(BoardMemory myBoard) {
		super(myBoard);
	}
	

	public void setAgent(Agent a){
		this.myAgent = a;
	}
	
	
	/**
	 * returns the next move object which maximizes our utility of getting to a terminal
	 * if we do not make the probability cut off or if no terminals are found, then we 
	 * return a nextDirObject, but we do not advise the move
	 *  --- if we dont use prob flag, this object says to kill self (but not advised to do so)
	 * @param param1	How long we are committed to this
	 * @param param2	The minimum probability for attempting to get to a terminal state, if < this we dont attempt it
	 * @param param3	1 = maximize probability, 2 utility with accurate board, 3=util pessimistic board, 4=util optimistic board
	 * @return nextMove
	 */
	public nextDirObject nextMove(double commitTime, double probCutOff, double FLAG){
		this.commitTime = (int) commitTime;
		setUpFields();
		this.setUpBMBoards();
		
		boolean foundTerm = getNearestTerminal((int)FLAG, probCutOff);
	
		if(ifNearestTerm == null){
			System.out.println("we have a null and we should not");
			ifNearestTerm = new nextDirObject();
			ifNearestTerm.adviseMove = false;
			ifNearestTerm.moveDirection = MoveTypes.KILL_SELF;
			return ifNearestTerm;
		}
		
		if(foundTerm == false){
			ifNearestTerm.adviseMove = false;
		}else{
			ifNearestTerm.adviseMove = true;
		}
		
	//	System.out.println("Its saying to " + MoveTypes.toString(ifNearestTerm.moveDirection));
		return ifNearestTerm;
	}
		
	
	/**
	 * The purpose of this method is to get to the nearest terminal
	 * if there are no terminals it will return false
	 * otherwise it returns true
	 *
	 * 	 the desired behavior is found in ifNearestTerm
	 * 
	 * the ifSuicide value should be compared with the value of ifNearestTerm
	 * -- might also want some idea of how many squares revealed
	 * @param FLAG  1 = maximize Probability, 2 = maximize Utility
	 * @param probCutOff Kill self if we can not reach square with certain probability
	 * @return	true if possible to reach a terminal state
	 */
	public boolean getNearestTerminal(int FLAG, double probCutOff){
		ifNearestTerm = null;
		/* if we want the highest prob of getting there */
		nextDirObject ifMaxProb = new nextDirObject();
		ifMaxProb.moveDirection = MoveTypes.NO_MOVE;
		ifMaxProb.probSuccess = 0;
		/* if we want to maximize our expected utility */
		nextDirObject maxUtility = new nextDirObject();
		maxUtility.moveDirection = MoveTypes.NO_MOVE;
		maxUtility.probSuccess = 0;
		boolean foundTerminal = false;
		
		/* loop through and see all of the terminal states 
		 * and find the maximum probability of reaching one of them, but we'll know which one
		 * or maximize our utility without knowing which one we are going to*/
		for(int r=0; r< maxRow; r++){
			for(int c=0; c<maxCol; c++){
				if(this.terminals[r][c]){
					/* we should see how likely it is to get here*/
					nextDirObject temp = this.probReachSquareFromTo(this.currRow, this.currColumn, r, c, this.numMovesRemaining);
					if(temp.probSuccess > .002){
						foundTerminal = true;
					}
					temp.destinationRow = r;
					temp.destinationColumn = c;
					if(temp.probSuccess > ifMaxProb.probSuccess){
						ifMaxProb = temp;
					}
				}
			}
		}
		
		nextDirObject maxExUtil = null;
		/* This says just generically, taking everything into account, where is my utility the greatest */
		if(FLAG == 1){
			maxExUtil = ifMaxProb;
		}else if(FLAG == 2){
			maxExUtil = this.getMaxUtil(this.theBoard, this.currRow, this.currColumn);
		}else if(FLAG == 3){
			maxExUtil = this.getMaxUtil(this.pessimisticBoard, this.currRow, this.currColumn);
		}else if(FLAG == 4){
			maxExUtil = this.getMaxUtil(this.goodBoard, this.currRow, this.currColumn);
		}
		
		/* store the value that we want to get to into ifNearestTerm */
		this.ifNearestTerm = maxExUtil;
		
		// if we haven't found a terminal then we should not attempt to get there 
		return foundTerminal;
	}
	
	
	
	/**
	 * This returns the maximum utility of what we should do from our square
	 * It uses an accurate penalty of death
	 * @param row
	 * @param col
	 * @return
	 */
	public nextDirObject getMaxUtil(double[][] board, int row, int col){
		return getNextDirection(board, terminals, walls, null, null, row, col,
				(int)Math.min(this.commitTime,this.numMovesRemaining), this.penaltyDeath, this.maxRow, this.maxCol,
				this.numRoundsRemaining,0);
	}

}
